package handler

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"
	"project-info/src/lib"
	"project-info/src/model"
	"project-info/src/service"
)

// OIDC服务实例
var oidcService = service.NewOIDCService()

// Authorization 授权端点
// GET/POST /oidc/auth
func Authorization(c *lib.GinContext) (err error) {
	if c.Request().Method == "POST" {
		return handleLogin(c)
	}
	return handleAuthRequest(c)
}

// handleAuthRequest 处理授权请求
func handleAuthRequest(c *lib.GinContext) error {
	// 获取请求参数
	responseType := c.QueryParam("response_type")
	clientID := c.QueryParam("client_id")
	redirectURI := c.QueryParam("redirect_uri")
	scope := c.QueryParam("scope")
	state := c.QueryParam("state")
	nonce := c.QueryParam("nonce")
	codeChallenge := c.QueryParam("code_challenge")
	codeChallengeMethod := c.QueryParam("code_challenge_method")

	// 验证必需参数
	if responseType == "" {
		return redirectError(c, redirectURI, "invalid_request", "Missing response_type parameter", state)
	}
	if clientID == "" {
		return redirectError(c, redirectURI, "invalid_request", "Missing client_id parameter", state)
	}
	if redirectURI == "" {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{
			"error":             "invalid_request",
			"error_description": "Missing redirect_uri parameter",
		})
	}

	// 验证客户端
	client, err := oidcService.GetClient(clientID)
	if err != nil {
		return redirectError(c, redirectURI, "invalid_client", err.Error(), state)
	}

	// 验证重定向URI
	if !oidcService.ValidateRedirectURI(client, redirectURI) {
		return redirectError(c, redirectURI, "invalid_request", "Invalid redirect_uri", state)
	}

	// 验证响应类型
	if responseType != "code" {
		return redirectError(c, redirectURI, "unsupported_response_type", "Only 'code' response type is supported", state)
	}

	// 验证作用域
	if scope == "" {
		return redirectError(c, redirectURI, "invalid_request", "Missing scope parameter", state)
	}
	if !strings.Contains(scope, "openid") {
		return redirectError(c, redirectURI, "invalid_scope", "'openid' scope is required", state)
	}

	// 验证请求的作用域是否在客户端允许范围内
	requestedScopes := oidcService.ParseScope(scope)
	if !oidcService.ValidateScope(client, requestedScopes) {
		return redirectError(c, redirectURI, "invalid_scope", "Requested scope exceeds client allowed scope", state)
	}

	// 检查用户是否已登录（简化实现，检查session cookie）
	sessionID, err := c.Cookie("oidc_session")
	if err != nil || sessionID == "" {
		// 用户未登录，显示登录页面
		return showLoginPage(c, clientID, redirectURI, scope, state, nonce, codeChallenge, codeChallengeMethod)
	}

	// 检查会话是否有效
	session, err := oidcService.GetUserSession(sessionID)
	if err != nil {
		// 会话无效，显示登录页面
		return showLoginPage(c, clientID, redirectURI, scope, state, nonce, codeChallenge, codeChallengeMethod)
	}

	// 用户已登录，生成授权码
	authCode, err := generateAuthCode()
	if err != nil {
		return redirectError(c, redirectURI, "server_error", "Failed to generate authorization code", state)
	}

	// 创建授权码记录
	authCodeRecord := &model.OIDCAuthorizationCode{
		Code:                authCode,
		ClientID:            clientID,
		UserID:              session.UserID,
		UserName:            session.UserName,
		RedirectURI:         redirectURI,
		Scope:               scope,
		State:               state,
		Nonce:               nonce,
		CodeChallenge:       codeChallenge,
		CodeChallengeMethod: codeChallengeMethod,
		ExpiresAt:           time.Now().Add(time.Duration(model.GloOIDC.AuthorizationCodeTTL) * time.Second),
	}

	// 保存到数据库
	err = oidcService.CreateAuthorizationCode(authCodeRecord)
	if err != nil {
		return redirectError(c, redirectURI, "server_error", "Failed to store authorization code", state)
	}

	// 重定向到客户端
	redirectURL := fmt.Sprintf("%s?code=%s", redirectURI, authCode)
	if state != "" {
		redirectURL += "&state=" + url.QueryEscape(state)
	}

	c.Redirect(http.StatusFound, redirectURL)
	return nil
}

// handleLogin 处理用户登录
func handleLogin(c *lib.GinContext) error {
	username := c.PostForm("username")
	password := c.PostForm("password")
	clientID := c.PostForm("client_id")
	redirectURI := c.PostForm("redirect_uri")
	scope := c.PostForm("scope")
	state := c.PostForm("state")
	nonce := c.PostForm("nonce")
	codeChallenge := c.PostForm("code_challenge")
	codeChallengeMethod := c.PostForm("code_challenge_method")

	if username == "" || password == "" {
		return showLoginPage(c, clientID, redirectURI, scope, state, nonce, codeChallenge, codeChallengeMethod)
	}

	// 使用数据库进行用户验证
	userID, err := authenticateUser(username, password)
	if err != nil {
		// 认证失败，重新显示登录页面
		return showLoginPage(c, clientID, redirectURI, scope, state, nonce, codeChallenge, codeChallengeMethod)
	}

	// 创建用户会话
	sessionID, err := generateSessionID()
	if err != nil {
		return redirectError(c, redirectURI, "server_error", "Failed to create session", state)
	}

	now := time.Now()
	// 创建会话记录
	sessionRecord := &model.OIDCUserSession{
		SessionID: sessionID,
		UserID:    userID,
		UserName:  username,
		ClientSessions: map[string]interface{}{
			clientID: map[string]interface{}{
				"loginTime": now,
				"scopes":    strings.Fields(scope),
			},
		},
		LoginTime:    now,
		LastActivity: now,
		ExpiresAt:    now.Add(time.Duration(model.GloOIDC.SessionTTL) * time.Second),
		IPAddress:    c.Request().RemoteAddr,
		UserAgent:    c.Request().UserAgent(),
	}

	// 保存到数据库
	err = oidcService.CreateUserSession(sessionRecord)
	if err != nil {
		c.Logger().Error(err.Error())
		return redirectError(c, redirectURI, "server_error", "Failed to create session", state)
	}

	// 设置会话cookie
	oidcConfig := model.GloOIDC
	c.SetCookie(
		"oidc_session",
		sessionID,
		int(time.Duration(oidcConfig.SessionTTL) * time.Second / time.Second),
		"/",
		"",
		false,
		true,
	)

	// 生成授权码
	authCode, err := generateAuthCode()
	if err != nil {
		return redirectError(c, redirectURI, "server_error", "Failed to generate authorization code", state)
	}

	// 创建授权码记录
	authCodeRecord := &model.OIDCAuthorizationCode{
		Code:                authCode,
		ClientID:            clientID,
		UserID:              userID,
		UserName:            username,
		RedirectURI:         redirectURI,
		Scope:               scope,
		State:               state,
		Nonce:               nonce,
		CodeChallenge:       codeChallenge,
		CodeChallengeMethod: codeChallengeMethod,
		ExpiresAt:           now.Add(time.Duration(model.GloOIDC.AuthorizationCodeTTL) * time.Second),
	}

	// 保存到数据库
	err = oidcService.CreateAuthorizationCode(authCodeRecord)
	if err != nil {
		return redirectError(c, redirectURI, "server_error", "Failed to store authorization code", state)
	}

	// 重定向到客户端
	redirectURL := fmt.Sprintf("%s?code=%s", redirectURI, authCode)
	if state != "" {
		redirectURL += "&state=" + url.QueryEscape(state)
	}

	c.Redirect(http.StatusFound, redirectURL)
	return nil
}

// showLoginPage 显示登录页面
func showLoginPage(c *lib.GinContext, clientID, redirectURI, scope, state, nonce, codeChallenge, codeChallengeMethod string) error {
	// 获取客户端信息
	client, err := oidcService.GetClient(clientID)
	var clientName string
	if err != nil {
		clientName = "未知应用"
	} else {
		clientName = client.ClientName
	}

	// 返回登录页面HTML
	html := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head>
    <title>OIDC 登录</title>
    <meta charset="utf-8">
    <style>
        body { font-family: Arial, sans-serif; margin: 50px; }
        .login-form { max-width: 400px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; }
        input[type="text"], input[type="password"] { width: 100%%; padding: 8px; border: 1px solid #ddd; border-radius: 3px; }
        button { background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 3px; cursor: pointer; }
        button:hover { background-color: #0056b3; }
        .client-info { background-color: #f8f9fa; padding: 10px; border-radius: 3px; margin-bottom: 20px; }
    </style>
</head>
<body>
    <div class="login-form">
        <div class="client-info">
            <strong>%s</strong> 请求访问您的账户
        </div>
        <form method="post">
            <div class="form-group">
                <label for="username">用户名:</label>
                <input type="text" id="username" name="username" required>
            </div>
            <div class="form-group">
                <label for="password">密码:</label>
                <input type="password" id="password" name="password" required>
            </div>
            <input type="hidden" name="client_id" value="%s">
            <input type="hidden" name="redirect_uri" value="%s">
            <input type="hidden" name="scope" value="%s">
            <input type="hidden" name="state" value="%s">
            <input type="hidden" name="nonce" value="%s">
            <input type="hidden" name="code_challenge" value="%s">
            <input type="hidden" name="code_challenge_method" value="%s">
            <button type="submit">登录</button>
        </form>
        <p><small>请使用系统中已注册的用户账户登录</small></p>
    </div>
</body>
</html>
`, clientName, clientID, redirectURI, scope, state, nonce, codeChallenge, codeChallengeMethod)

	c.HTML(http.StatusOK, html, nil)
	return nil
}

// authenticateUser 使用数据库验证用户凭据
func authenticateUser(username, password string) (uint64, error) {
	var user model.User
	err := model.DB().Where("username = ? and password = ? and deleted=0", username, lib.Md5(password)).First(&user).Error
	if err != nil {
		return 0, fmt.Errorf("用户名或密码错误")
	}
	if user.Id == 0 {
		return 0, fmt.Errorf("用户名或密码错误")
	}
	return uint64(user.Id), nil
}

// generateAuthCode 生成授权码
func generateAuthCode() (string, error) {
	bytes := make([]byte, 32)
	_, err := rand.Read(bytes)
	if err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(bytes), nil
}

// generateSessionID 生成会话ID
func generateSessionID() (string, error) {
	bytes := make([]byte, 32)
	_, err := rand.Read(bytes)
	if err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(bytes), nil
}

// getSameSitePolicy 获取SameSite cookie策略
func getSameSitePolicy(policy string) http.SameSite {
	switch strings.ToLower(policy) {
	case "strict":
		return http.SameSiteStrictMode
	case "lax":
		return http.SameSiteLaxMode
	case "none":
		return http.SameSiteNoneMode
	default:
		return http.SameSiteLaxMode
	}
}

// redirectError 重定向错误
func redirectError(c *lib.GinContext, redirectURI, errorCode, errorDescription, state string) error {
	if redirectURI == "" {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{
			"error":             errorCode,
			"error_description": errorDescription,
		})
	}

	errorURL := fmt.Sprintf("%s?error=%s&error_description=%s", redirectURI, errorCode, url.QueryEscape(errorDescription))
	if state != "" {
		errorURL += "&state=" + url.QueryEscape(state)
	}

	c.Redirect(http.StatusFound, errorURL)
	return nil
}
